//Otimizações:
//OT-01#

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>

#include "glslcompiler.h"
#include "matgl.h"
#include "camera.h"
#include "control.h"
#include "stb_image.h"

#include "objloader.h"
#include "meshloader.h"
#include <assert.h>


#define PI 3.14159265359

#define OBJ_FILE "blackjack7.obj"
#define PNG_FILE "pumpkin.png"

int main()
{
	glfwInit();
	GLFWwindow* window = glfwCreateWindow (640, 480, "OpenGL teste", NULL, NULL);
	glfwMakeContextCurrent(window);
	glfwSetKeyCallback(window, KeyboardCallback);
	glfwSetCursorPosCallback(window, MousePosCallback);
	glewInit();
	CCamera camera;
	BindControlToCamera(&camera);

	glEnable(GL_DEPTH_TEST); //enable depth-testing
	glDepthFunc(GL_LESS); //depth-testing interprets a smaller value as "closer"
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	FILE* vs_file = fopen("src/vertex_shader.glsl","r");
	FILE* fs_file = fopen("src/fragment_shader.glsl","r");
	unsigned int shader_programme = glslCompile(vs_file, fs_file);
	fclose(vs_file);
	fclose(fs_file);


	// Load da textura -- devemos analisar. ////////////////////////////////////////////////////////////////////////
	int x,y,z,n, force_channels=4;
	unsigned char* image_data = stbi_load (PNG_FILE, &x, &y, &n, force_channels);
	if (!image_data) {
		fprintf (stderr, "ERROR: could not load %s\n", PNG_FILE);
		return 1;
	}
	////// FLIP NA IMAGEM
	int width_in_bytes = x * 4;
	unsigned char *top = NULL;
	unsigned char *bottom = NULL;
	unsigned char temp = 0;
	int half_height = y / 2;

	for (int row = 0; row < half_height; row++) {
		top = image_data + row * width_in_bytes;
		bottom = image_data + (y - row - 1) * width_in_bytes;
		for (int col = 0; col < width_in_bytes; col++) {
			temp = *top;
			*top = *bottom;
			*bottom = temp;
			top++;
			bottom++;
		}
	}
	////////////////////////////
	
	unsigned int tex = 0;
	glGenTextures(1, &tex); // Gera textura no endereço.
	glActiveTexture(GL_TEXTURE0); // Define textura para trabalhar.
	glBindTexture(GL_TEXTURE_2D, tex); // Põe textura em evidência na máquina de estados
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //// TOPICS LATER
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	
	int tex_loc = glGetUniformLocation (shader_programme, "basic_texture");
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	//Loader do Anton
	//=============================================
	unsigned int vao = 0;
	int pontos = 0;
	assert(load_mesh (OBJ_FILE, &vao, &pontos));
	printf("\nPontos: %d\n",pontos);
	//==============================================

// 	glBufferData(GL_ARRAY_BUFFER, 18*sizeof(float), NULL, GL_STATIC_DRAW);
// 	glBufferSubData(GL_ARRAY_BUFFER, 0, 9*sizeof(float),points);
// 	glBufferSubData(GL_ARRAY_BUFFER, 9*sizeof(float), 9*sizeof(float),colors);
// 	GLuint recebenumero = glGetAttribLocation(program,"nome da variavel"); //isso serve para descobrir o valor do primeiro parametro da função glVertexAtriibPointer e pra dar enable ou disable em algum atributo
// 	glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,BUFFER_OFFSET(0));
// 	glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,BUFFER_OFFSET(9*sizeof(float)));

	float* Matrix_Rotation = MatrixRotationX(0);
	GLint Matrix_Rotation_Locate = glGetUniformLocation(shader_programme,"Matrix_Rotation");

	int visao_mat_location = glGetUniformLocation (shader_programme, "visao");
	int projecao_mat_location = glGetUniformLocation (shader_programme, "projecao");
	
	clock_t clock_atual = clock();

	while (!glfwWindowShouldClose (window))
	{
		glClearColor(0.6f, 0.6f, 0.8f, 1.0f); //wipe the drawing surface clear
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glUseProgram (shader_programme);
		camera.View(visao_mat_location,projecao_mat_location);
		glUniformMatrix4fv(Matrix_Rotation_Locate, 1, GL_FALSE, Matrix_Rotation);
		glUniform1i (tex_loc, 0); // use active texture 0

		glBindVertexArray(vao);
		glDrawArrays (GL_TRIANGLES, 0, pontos); // draw points 0-3 from the currently bound VAO with current in-use shader

		glPolygonMode(GL_FRONT,GL_FILL);

		glfwSwapBuffers(window); //put the stuff we've been drawing onto the display
		glfwPollEvents(); //update other events like input handling
		
		if(clock() > clock_atual)
		{
			clock_atual = clock() + 0.01*CLOCKS_PER_SEC;
			TestFlag();
		}
	}
}
